#include <ani.h>
#include <array>
#include <iostream>
#include <vector>

std::string ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str)
{
    ani_size strSize;
    env->String_GetUTF8Size(ani_str, &strSize);

    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char *utf8_buffer = buffer.data();

    // String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written);

    utf8_buffer[bytes_written] = '\0';
    std::string content = std::string(utf8_buffer);
    return content;
}

void createDouble(ani_env *env){
    static const char *className = "std.core.Double;";
    ani_class persion_cls;
    if (ANI_OK != env->FindClass(className, &persion_cls))
    {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return;
    }
    ani_method personInfoCtor;
    env->Class_FindMethod(persion_cls, "<ctor>","d:", &personInfoCtor);
    ani_object personInfoObj;
    env->Object_New(persion_cls, personInfoCtor, &personInfoObj, ani_double(2.0));
}

static void handleData_optinal(ani_env *env)
{
    static const char *className = "ani_boxed_Double.PersonInfo";
    ani_class persion_cls;
    if (ANI_OK != env->FindClass(className, &persion_cls))
    {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return;
    }
    ani_method personInfoCtor;
    env->Class_FindMethod(persion_cls, "<ctor>","d:", &personInfoCtor);
    ani_object personInfoObj;
    env->Object_New(persion_cls, personInfoCtor, &personInfoObj);

    ani_double age_no_optional_value;
    env->Object_GetFieldByName_Double(personInfoObj, "age_no_optional",  &age_no_optional_value);
    std::cout << "age_no_optional_value value:" <<std::to_string(age_no_optional_value) << std::endl;

    ani_ref boxed_age_optional_ref;
    env->Object_GetFieldByName_Ref(personInfoObj, "age_optional", &boxed_age_optional_ref);

    ani_double age_optional_value;
    env->Object_CallMethodByName_Double(static_cast<ani_object>(boxed_age_optional_ref), "toDouble",":d" ,&age_optional_value);
    std::cout << "unboxed age_optional_value value:" <<std::to_string(age_optional_value) << std::endl;
}

ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;

    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env))
    {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    static const char *moduleName = "ani_boxed_Double";
    ani_module module;
    if (ANI_OK != env->FindModule(moduleName, &module))
    {
        std::cerr << "Not found '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function{"handleData", ":", reinterpret_cast<void *>(handleData_optinal)},
    };

    std::cout << "Start bind native methods to '" << moduleName << "'" << std::endl;

    if (ANI_OK != env->Module_BindNativeFunctions(module, methods.data(), methods.size()))
    {
        std::cerr << "Cannot bind native methods to '" << moduleName << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods to '" << moduleName << "'" << std::endl;

    *result = ANI_VERSION_1;
    return ANI_OK;
}